package cn.demoncat.util.http;

import cn.demoncat.util.exception.RestRuntimeException;
import cn.demoncat.util.lang.CollectionUtil;
import cn.demoncat.util.lang.IntegerUtil;
import cn.demoncat.util.web.constant.HttpConstant;
import cn.demoncat.util.web.entity.HttpParam;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Map;

/**
 * Jsoup工具
 * 
 * <pre>
 * 1、解析HTML为Document（Html文本、URL请求）
 * 2、获取和操作Element（类似JQuery语法）
 * </pre>
 * 
 * @author 延晓磊
 *
 * @since 2015年10月11日
 */
public final class JsoupUtil {

	private static final Logger LOG = LoggerFactory.getLogger(JsoupUtil.class);
	
	/**
	 * 解析HTML
	 * 
	 * @param html	HTML文本
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	public static Document html(String html) {
		return Jsoup.parse(html);
	}
 
	/**
	 * 解析HTML
	 * 
	 * @param html		HTML文本
	 * @param baseUrl	根URL（转换相对路径）
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	public static Document html(String html, String baseUrl){
		return Jsoup.parse(html, baseUrl);
	}
	
	/**
	 * GET请求
	 * 
	 * @param url
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	public static Document get(String url){
		return http(HttpParam.get(url));
	}
	
	/**
	 * GET请求
	 * 
	 * @param url
	 * @param params	参数
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	public static Document get(String url, Map<String, String> params){
		return http(HttpParam.get(url).addParam(params));
	}
	
	/**
	 * POST请求
	 * 
	 * @param url
	 * @param params	参数
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	public static Document post(String url, Map<String, String> params){
		return http(HttpParam.post(url).addParam(params));
	}
	
	/**
	 * HTTP请求
	 * 
	 * @param param	
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	public static Document http(HttpParam param){
		// 连接
		Connection connect = Jsoup.connect(param.getUrl());
		// 请求参数
		if (CollectionUtil.isNotEmpty(param.getParams())){
			connect.data(param.getParams());
		}
		// 请求头
		if (CollectionUtil.isNotEmpty(param.getHeaders())){
			connect.headers(param.getHeaders());
		}
		// Cookies
		if (CollectionUtil.isNotEmpty(param.getCookies())){
			connect.cookies(param.getCookies());
		}
		// 忽略SSL
		connect.sslSocketFactory(SslUtil.createIgnoreContext().getSocketFactory());
		// 发起请求
		return request(param.getMethod(), param.getUrl(), connect, param.getTimeout(), param.getTimes());
	}

	/**
	 * 获取元素 - ID选择器
	 * 
	 * @param el	Element/Document
	 * @param id	ID
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	public static Element getById(Element el, String id){
		return el.getElementById(id);
	}

	/**
	 * 获取元素 - 标签选择器
	 * 
	 * @param el	Element/Document
	 * @param tag	标签
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	public static Elements getByTag(Element el, String tag){
		return el.getElementsByTag(tag);
	}

	/**
	 * 获取元素 - 类选择器
	 * 
	 * @param el	Element/Document
	 * @param clazz	类名
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	public static Elements getByClass(Element el, String clazz){
		return el.getElementsByClass(clazz);
	}

	/**
	 * 获取元素 - 属性选择器
	 * 
	 * @param el	Element/Document
	 * @param attr	属性名	
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	public static Elements getByAttr(Element el, String attr){
		return el.getElementsByAttribute(attr);
	}

	/**
	 * 获取元素 - 属性选择器
	 * 
	 * @param el	Element/Document
	 * @param attr	属性名	
	 * @param val	属性值
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	public static Elements getByAttr(Element el, String attr, String val){
		return el.getElementsByAttributeValue(attr, val);
	}

	/**
	 * 获取元素 - 内容选择器
	 * 
	 * @param el	Element/Document
	 * @param regex	内容正则	
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	public static Elements getByReg(Element el, String regex){
		return el.getElementsMatchingText(regex);
	}

	/**
	 * 获取元素 - CSS选择器
	 * 
	 * @param el	Element/Document
	 * @param cs	CSS选择器	
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	public static Elements getBySelector(Element el, String cs){
		return el.select(cs);
	}
	
	/**
	 * 获取href属性
	 * 
	 * @param el
	 * 
	 * @return 绝对地址
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	public static String getHref(Element el) {
		return el.absUrl("href");
	}
	
	
	
	
	/**
	 * 发起请求
	 * 
	 * @param method	Method.GET/POST
	 * @param url
	 * @param connect
	 * @param timeout	超时秒数（默认30）
	 * @param times		重试次数
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2015年10月11日
	 */
	private static Document request(String method, String url, Connection connect, Integer timeout, Integer times) {
		LOG.info(HttpConstant.HTTP_SEND_LOG, method, url);
		// 超时
		if (!IntegerUtil.isEmpty(timeout)) {
			timeout = timeout * 1000;
			connect.timeout(timeout);
		}
		// 重试
		boolean isGet = method.equals(HttpConstant.Method.GET);
		if (times != null && times > 0) {
			// 发起请求
			for (int i = 1; i <= times; i++) {
				try {
					return isGet ? connect.get() : connect.post();
				} catch (IOException e) {
					if (i >= times) {
						// 最后一次
						throw RestRuntimeException.httpUrlError(url, e);
					} else {
						// 重试
						LOG.warn("请求失败，重新尝试：" + i, e);
					}
				}
			}
			return null;
		}else {
			// 发起请求
			try {
				return isGet ? connect.get() : connect.post();
			} catch (IOException e) {
				throw RestRuntimeException.httpUrlError(url, e);
			}
		}
	}
	
}
